package com.wd.jetpack.util.log4j;



import com.wd.jetpack.util.CommonMessageSource;

import org.apache.log4j.Logger;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @author caili
 */
public class LogFileUtils {

    public static FilenameFilter javaFileFilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return name.endsWith(".java");
        }
    };

    public static FilenameFilter dirFilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return new File(dir, name).isDirectory();
        }
    };

    public static FilenameFilter dirHasJavaFilesFilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            File file = new File(dir, name);
            if (file.isDirectory()) {
                File[] files = file.listFiles(javaFileFilter);
                return files != null && files.length > 0;
            }
            return false;
        }
    };

    /**
     * 搜索file目录下的文件(包括目录), 返回满足filter的文件或目录的列表. 如果file是一个文件, 则返回此文件.
     *
     * @param file
     * @param filter    null表示不过滤
     * @param recursive 是否递归搜索
     * @return List of File
     */
    public static List<File> listFiles(File file, FilenameFilter filter, boolean recursive) {
        List<File> list = new ArrayList<File>();
        if (file.isFile() && (filter == null || filter.accept(file.getParentFile(), file.getName()))) {
            list.add(file);
        } else {
            File[] files = file.listFiles();
            for (int i = 0; files != null && i < files.length; ++i) {
                if (filter == null || filter.accept(files[i].getParentFile(), files[i].getName())) {
                    list.add(files[i]);
                }
                if (recursive && files[i].isDirectory()) {
                    list.addAll(listFiles(files[i], filter, recursive));
                }
            }
        }
        return list;
    }

    /**
     * 删除文件或目录. 如果是目录的话, 递归删除这个目录中所有的文件和目录
     *
     * @param file
     * @return
     */
    public static boolean deleteFile(File file) {
        if (!file.exists()) {
            return true;
        }

        if (file.isFile()) {
            return file.delete();
        } else {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; ++i) {
                if (!deleteFile(files[i])) {
                    return false;
                }
            }
            return file.delete();
        }
    }

    public static boolean deleteSubFile(File file) {
        if (!file.exists()) {
            return true;
        }

        if (file.isFile()) {
            return false;
        }

        File[] files = file.listFiles();
        for (int i = 0; i < files.length; ++i) {
            if (!deleteFile(files[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 取得目录dir中最新的limit个文件.
     *
     * @param dir
     * @param limit
     * @param recursive 是否递归包含子目录
     * @return list of File
     */
    public static List getLastestFiles(File dir, FilenameFilter filter, int limit, boolean recursive) {
        List files = listFiles(dir, filter, recursive);
        // 按时间倒序排序文件
        for (int i = 0; i < files.size() - 1; ++i) {
            for (int j = i + 1; j < files.size(); ++j) {
                File a = (File) files.get(i);
                File b = (File) files.get(j);
                if (a.lastModified() < b.lastModified()) {
                    files.set(i, b);
                    files.set(j, a);
                }
            }
        }
        List result = new ArrayList();
        for (int i = 0; result.size() < limit && i < files.size(); ++i) {
            if (((File) files.get(i)).isFile()) {
                result.add(files.get(i));
            }
        }
        return result;
    }

    public static List getLastestFiles(File dir, int limit, boolean recursive) {
        return getLastestFiles(dir, null, limit, recursive);
    }

    public static List getLastestFilesExclude(File dir, String[] excludeDirs, int limit, boolean recursive) {
        return getLastestFiles(dir, new ExcludeDirFilter(excludeDirs), limit, recursive);
    }

    public static byte[] readStreamBytes(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int bytesRead = -1;
        while (-1 != (bytesRead = in.read(b, 0, b.length))) {
            out.write(b, 0, bytesRead);
        }
        out.flush();
        byte[] result = out.toByteArray().clone();
        in.close();
        out.close();
        return result;
    }

    public static String readStreamString(InputStream in) throws IOException {
        return new String(readStreamBytes(in));
    }

    public static String readStreamString(InputStream in, String encoding) throws IOException {
        return new String(readStreamBytes(in), encoding);
    }

    public static List<String> readStreamListOfString(InputStream in, String encoding) throws IOException {
        List<String> lines = new ArrayList<String>();
        LineNumberReader reader = new LineNumberReader(new InputStreamReader(in, encoding));
        String line = reader.readLine();
        while (line != null) {
            lines.add(line);
            line = reader.readLine();
        }
        return lines;
    }

    public static long getFileSize(File file) {
        if (file.isFile()) {
            return file.length();
        }
        List<File> files = listFiles(file, null, true);
        long size = 0;
        for (File f : files) {
            size += f.length();
        }
        return size;
    }

    public static void extract(ClassLoader cl, String resource, File diskFile) throws IOException {
        Logger.getLogger(LogFileUtils.class).debug("extracting " + resource + " to " + diskFile.getAbsolutePath());
        InputStream in = null;
        FileOutputStream out = null;
        try {
            in = cl.getResourceAsStream(resource);
            if (in == null) {
                throw new IOException(CommonMessageSource.getMessage("FileUtils.resource.error", "Resource not found ")
                        + ":" + resource);
            }
            File dest = diskFile;
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            out = new FileOutputStream(dest);
            byte[] b = new byte[16 * 1024];
            for (int c = in.read(b); c != -1; c = in.read(b)) {
                out.write(b, 0, c);
            }
            out.flush();
            out.getFD().sync();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                }
            }
        }
    }

    public static void extract(ClassLoader cl, String fn, String home) throws IOException {
        Logger.getLogger(LogFileUtils.class).debug("extracting " + fn + " to " + home + "/" + fn);
        InputStream in = null;
        FileOutputStream out = null;
        try {
            in = cl.getResourceAsStream(fn);
            if (in == null) {
                throw new IOException(CommonMessageSource.getMessage("FileUtils.resource.error", "Resource not found ")
                        + ":" + fn);
            }
            File dest = new File(home, fn);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            out = new FileOutputStream(new File(home, fn));
            byte[] b = new byte[16 * 1024];
            for (int c = in.read(b); c != -1; c = in.read(b)) {
                out.write(b, 0, c);
            }
            out.flush();
            out.getFD().sync();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * 将file copy 到 toDir目录下
     *
     * @param cl
     * @param filePathName
     * @param fileName
     * @param toDir
     * @throws
     */
    public static void extract(ClassLoader cl, String filePathName, String fileName, String toDir) throws IOException {
        Logger.getLogger(LogFileUtils.class).debug("extracting " + filePathName + " to " + toDir + "/" + fileName);
        InputStream in = null;
        FileOutputStream out = null;
        try {
            in = cl.getResourceAsStream(filePathName);
            if (in == null) {
                throw new IOException(CommonMessageSource.getMessage("FileUtils.resource.error", "Resource not found ")
                        + ":" + filePathName);
            }
            File dest = new File(toDir, fileName);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            out = new FileOutputStream(dest);
            byte[] b = new byte[16 * 1024];
            for (int c = in.read(b); c != -1; c = in.read(b)) {
                out.write(b, 0, c);
            }
            out.flush();
            out.getFD().sync();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * 将source压缩到dest.
     *
     * @param source
     * @param dest
     * @throws
     */
    public static void zip(File source, File dest, IProgressMonitor pm) throws Exception {
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        if (pm != null) {
            pm.println(CommonMessageSource.getMessage("FileUtils.compression", "压缩 ") + source.getAbsolutePath()
                    + CommonMessageSource.getMessage("FileUtils.arrive", " 到 ") + dest.getAbsolutePath());
        }
        FileOutputStream fos = new FileOutputStream(dest);
        ZipOutputStream out = new ZipOutputStream(fos);
        zip(out, source, "", pm);
        out.finish();
        out.flush();
        fos.getFD().sync();
        out.close();
        if (pm != null) {
            pm.println(CommonMessageSource.getMessage("FileUtils.finished.compression", "压缩 完成"));
        }
    }

    private static void zip(ZipOutputStream out, File f, String base, IProgressMonitor pm) throws Exception {
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
            // out.putNextEntry(new ZipEntry(base + "/"));
            base = base.length() == 0 ? "" : base + "/";
            for (int i = 0; i < fl.length; i++) {
                zip(out, fl[i], base + fl[i].getName(), pm);
            }
        } else {
            ZipEntry ze = new ZipEntry(base);
            ze.getCompressedSize();
            ze.setTime(f.lastModified());
            out.putNextEntry(ze);
            FileInputStream in = new FileInputStream(f);
            byte[] b = new byte[32 * 1024];
            int len = -1;
            while ((len = in.read(b, 0, b.length)) != -1) {
                out.write(b, 0, len);
            }
            in.close();
            out.closeEntry();
            if (pm != null) {
                long ratio = 0;
                if (f.length() != 0) {
                    ratio = 100 - ze.getCompressedSize() * 100 / f.length();
                }
                pm.println(CommonMessageSource.getMessage("FileUtils.add", "增加 ") + base
                        + CommonMessageSource.getMessage("FileUtils.read", " 读入") + f.length()
                        + CommonMessageSource.getMessage("FileUtils.write", " 写出") + ze.getCompressedSize()
                        + CommonMessageSource.getMessage("FileUtils.compression", "压缩 ") + ratio + "%");
            }
        }
    }

    /**
     * zipFile必须存在. zipFile中不带子目录. toDir中如果存在同名文件则被覆盖.
     *
     * @param zipFile
     * @param toDir               解压缩到这个目录. 如果不存在则创建.
     * @param fileNamaToUpperCase 是否设置解压后zip内容文件名一个.之前的名称成大写:false=不设置大写,true=设置大写
     * @throws
     */
    public static void unzip(File zipFile, File toDir, boolean fileNamaToUpperCase) throws Exception {
        Logger logger = Logger.getLogger(LogFileUtils.class);
        logger.info("unzip " + zipFile.getAbsolutePath() + " to " + toDir.getAbsolutePath());
        if (!toDir.exists()) {
            toDir.mkdirs();
        }
        ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
        FileOutputStream os = null;
        try {

            ZipFile zf = null;
            try {
                zf = new ZipFile(zipFile);
            } catch (Exception e) {
                throw new Exception(CommonMessageSource.getMessage("FileUtils.unknown.zipType", "不认识的压缩格式") + "(" + e + ")");
            } finally {
                if (zf != null) {
                    try {
                        zf.close();
                    } catch (Exception e) {
                    }
                }
            }

            for (ZipEntry entry = zis.getNextEntry(); entry != null; entry = zis.getNextEntry()) {
                if (entry.isDirectory()) {
                    throw new Exception(zipFile.getName()
                            + CommonMessageSource.getMessage("FileUtils.unknown.fileDir", "中不应存在目录."));
                }

                StringBuffer entryFileNameSb = new StringBuffer();
                if (fileNamaToUpperCase) {
                    int index = entry.getName().indexOf('.');
                    if (index > 0) {
                        entryFileNameSb.append(entry.getName().substring(0, index).toUpperCase());
                        entryFileNameSb.append(entry.getName().substring(index));
                    } else {
                        entryFileNameSb.append(entry.getName());
                    }
                } else {
                    entryFileNameSb.append(entry.getName());
                }

                String entryFileName = entryFileNameSb.toString();
                logger.debug("expanding " + entryFileName);
                File toFile = new File(toDir, entryFileName);
                if (toFile.getParentFile() == null || !toFile.getParentFile().exists()) {
                    toFile.getParentFile().mkdirs();
                }

                try {
                    os = new FileOutputStream(toFile);
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    while ((bytesRead = zis.read(buffer)) != -1) {
                        os.write(buffer, 0, bytesRead);
                    }
                    os.flush();
                    os.getFD().sync();
                } finally {
                    if (os != null) {
                        try {
                            os.close();
                        } catch (Exception e) {
                        }
                    }
                    os = null;
                }
                toFile.setLastModified(entry.getTime());
            }
        } catch (Exception e) {
            logger.warn("解压错误: ", e);
            throw e;
        } finally {
            if (zis != null) {
                try {
                    zis.close();
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * zipFile必须存在. zipFile中不带子目录. toDir中如果存在同名文件则被覆盖.
     *
     * @param zipFile
     * @param toDir   解压缩到这个目录. 如果不存在则创建.
     * @throws
     */
    public static void unzip(File zipFile, File toDir) throws Exception {
        unzip(zipFile, toDir, false);
    }

    /**
     * 检查指定文件在指定时间后是否更新过, 有更新则返回最新内容, 未更新则返回null, 不存在则返回空字符串.
     *
     * @param file
     * @param lastModified
     * @return
     * @throws
     */
    public static String readNewerFile(File file, long lastModified) throws Exception {
        Logger logger = Logger.getLogger(LogFileUtils.class);
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader reader = null;
        try {
            logger.debug("file:" + file.getAbsolutePath());
            if (!file.exists()) {
                logger.debug(file.getAbsolutePath() + " not exist!");
                return "";
            }

            // 比较两个时间
            if (lastModified >= file.lastModified()) {
                logger.debug("timecheck:" + lastModified + ":" + file.lastModified());
                return null;
            }

            StringBuffer result = new StringBuffer();
            fis = new FileInputStream(file);
            isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
            reader = new BufferedReader(isr);
            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
                result.append(line);
                result.append("\n");
            }
            return result.toString();
        } catch (Exception e) {
            logger.error("read file exception.", e);
            throw e;
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                logger.error("close file exception.", e);
            }
        }
    }

    public static String readFileContent(File file, String charsetName) throws Exception {
        Logger logger = Logger.getLogger(LogFileUtils.class);
        if (!file.exists()) {
            return null;
        }
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader reader = null;
        try {
            fis = new FileInputStream(file);
            isr = new InputStreamReader(fis, charsetName == null ? "UTF-8" : charsetName);
            reader = new BufferedReader(isr);
            StringBuffer result = new StringBuffer();
            String line = reader.readLine();
            while (line != null) {
                result.append(line);
                result.append("\n");
                line = reader.readLine();
            }
            return result.toString();
        } catch (Exception e) {
            logger.error("read file exception.", e);
            throw e;
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                logger.error("close file exception.", e);
            }
        }
    }

    /**
     * 读取文件内容, 文件不存在则返回null.
     *
     * @param file
     * @return
     * @throws
     */
    public static String readFileContent(File file) throws Exception {
        return readFileContent(file, null);
    }

    public static List<String> readFileContentLines(File file) throws Exception {
        return readFileContentLines(file, "UTF-8");
    }

    /**
     * 读取文件内容, 文件不存在则返回null.
     *
     * @param file
     * @return
     * @throws
     */
    public static List<String> readFileContentLines(File file, String charsetName) throws Exception {
        Logger logger = Logger.getLogger(LogFileUtils.class);
        List<String> strLines = new ArrayList<String>();
        if (!file.exists()) {
            return null;
        }
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader reader = null;
        try {
            fis = new FileInputStream(file);
            if (charsetName == null) {
                isr = new InputStreamReader(fis);
            } else {
                isr = new InputStreamReader(fis, charsetName);
            }
            reader = new BufferedReader(isr);
            String line = reader.readLine();
            while (line != null) {
                strLines.add(line);
                line = reader.readLine();
            }
            return strLines;
        } catch (Exception e) {
            logger.error("read file exception.", e);
            throw e;
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                logger.error("close file exception.", e);
            }
        }
    }

    /**
     * 写入文件内容
     *
     * @param file
     * @param content
     * @throws
     */
    public static void writeFileContent(File file, String content) throws Exception {
        Logger logger = Logger.getLogger(LogFileUtils.class);
        FileOutputStream fos = null;
        OutputStreamWriter writer = null;
        try {
            file.getParentFile().mkdirs();
            fos = new FileOutputStream(file);
            writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            writer.write(content);
            file.setLastModified(System.currentTimeMillis());
            writer.flush();
            fos.getFD().sync();
        } catch (Exception e) {
            logger.error("write file exception.", e);
            throw e;
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                logger.error("close file exception.", e);
            }
        }
    }

    /**
     * 检查dataFile的MD5摘要和md5File中内容是否相等. 如果md5File==null或不存在, 认为相等.
     *
     * @param dataFile
     * @param md5File
     * @return
     * @throws
     */
    public static boolean verifyMd5(File dataFile, File md5File) throws Exception {
        if (md5File == null || !md5File.exists()) {
            return true;
        }
        String fd = "";
        FileInputStream is = new FileInputStream(md5File);
        try {
            byte[] b = new byte[32];
            is.read(b);
            fd = new String(b, StandardCharsets.UTF_8);
        } finally {
            is.close();
        }
        String d = md5(dataFile);
        return d.equals(fd);
    }

    // ////////////////////////////////////////////////////
    //
    // 文件摘要
    //
    // ////////////////////////////////////////////////////

    /**
     * 对dataFile做md5摘要存入文件md5File中.
     *
     * @param dataFile
     * @param md5File
     * @throws
     */
    public static void createMd5(File dataFile, File md5File) throws Exception {
        String d = md5(dataFile);
        if (!md5File.getParentFile().exists()) {
            md5File.getParentFile().mkdirs();
        }
        FileOutputStream os = new FileOutputStream(md5File);
        try {
            os.write(d.getBytes(StandardCharsets.UTF_8));
            os.flush();
            os.getFD().sync();
        } finally {
            os.close();
        }
    }

    public static String md5(File file) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        int len;
        byte[] b = new byte[8 * 1024];
        FileInputStream in = new FileInputStream(file);
        try {
            while (-1 != (len = in.read(b, 0, b.length))) {
                md.update(b, 0, len);
            }
        } finally {
            in.close();
        }
        byte[] d = md.digest();
        StringBuilder sb = new StringBuilder();
        for (byte i : d) {
            String s = Integer.toHexString(i & 0x000000ff);
            if (s.length() < 2) {
                sb.append("0");
            }
            sb.append(s);
        }
        return sb.toString();
    }

    public static void copyDirectiory(File sourceDir, File destDir, IProgressMonitor pm) throws IOException {
        if (!sourceDir.exists()) {
            return;
        }

        if (!destDir.exists()) {
            destDir.mkdirs();
        }

        File[] file = sourceDir.listFiles();
        for (int i = 0; i < file.length; i++) {
            if (file[i].isFile()) {
                File destFile = new File(destDir, file[i].getName());
                if (pm != null) {
                    pm.println(CommonMessageSource.getMessage("FileUtils.copyFile", "copy file") + ":"
                            + file[i].getAbsolutePath() + " to " + destFile.getAbsolutePath());
                }
                copyFile(file[i], destFile);
            } else if (file[i].isDirectory()) {
                File nextSourceDir = new File(sourceDir, file[i].getName());
                File nextDestDir = new File(destDir, file[i].getName());
                if (pm != null) {
                    pm.println(CommonMessageSource.getMessage("FileUtils.copyDir", "copy dir") + ":"
                            + nextSourceDir.getAbsolutePath() + " to " + nextDestDir.getAbsolutePath());
                }
                copyDirectiory(nextSourceDir, nextDestDir, pm);
            }
        }
    }

    public static void copyFile(File sourceFile, File targetFile) throws IOException {
        FileInputStream input = new FileInputStream(sourceFile);
        BufferedInputStream inBuff = new BufferedInputStream(input);

        FileOutputStream output = new FileOutputStream(targetFile);
        BufferedOutputStream outBuff = new BufferedOutputStream(output);

        // 缓冲数组
        byte[] b = new byte[1024 * 5];
        int len;
        while ((len = inBuff.read(b)) != -1) {
            outBuff.write(b, 0, len);
        }
        outBuff.flush();

        // 关闭流
        inBuff.close();
        outBuff.close();
        output.close();
        input.close();
    }

    public static void copyFile(InputStream in, File targetFile) throws IOException {
        FileOutputStream fos = new FileOutputStream(targetFile);
        int len;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
            fos.flush();
        }
        fos.close();
    }

    public static boolean isUtf8(File file) throws IOException {
        if (file == null || !file.exists() || file.isDirectory()) {
            return false;
        }

        InputStream in = new FileInputStream(file);
        byte[] b = new byte[3];
        in.read(b);
        in.close();
        return b[0] == -17 && b[1] == -69 && b[2] == -65;
    }

    private static class ExcludeDirFilter implements FilenameFilter {
        private String[] dirs;

        public ExcludeDirFilter(String[] dirs) {
            this.dirs = dirs;
        }

        public boolean accept(File dir, String name) {
            if (dirs == null) {
                return true;
            }
            File file = new File(dir, name);
            for (int i = 0; i < dirs.length; ++i) {
                if (file.getAbsolutePath().startsWith(dirs[i])) {
                    return false;
                }
            }
            return true;
        }
    }

}